#include "data.h"
#include <QTime>
#include <QDebug>
#include <QThread>
/* 定时器 timer, 在GUI线程中.(因为在data类的构造函数中)
 * 在data线程中无法进行start, stop.(会提示无法打开或关闭另一线程中的定时器)
 * 若要使 timer 在data线程中, 则需要将其的定义移到槽函数中.
 */
Data::Data(QObject *parent) :
    QObject(parent)
{
    qDebug() << " gouzhao_dataThreadID:" << QThread::currentThreadId();
    blockTypes = 7; //有7种块
    grade = 0;  //成绩为0
    flag_gameRunning = false;   //初始化游戏运行标志位
    eachBlockTypes = new int[blockTypes]{2, 4, 2, 2, 4, 4, 1};   //初始化每种块的形状数
    timerNum = 200; //定时器定时时间
    timer = new QTimer;
    connect(timer, SIGNAL(timeout()), this, SLOT(slot_timeOut()));
    connect(this, SIGNAL(timerStart(int)), timer, SLOT(start(int)));
    connect(this, SIGNAL(timerStop()), timer, SLOT(stop()));
}
Data::~Data()
{
    qDebug() << "xigou_dataThreadID:" << QThread::currentThreadId();
   // timer->stop();
    emit timerStop();
    delete timer;
    delete [] eachBlockTypes;
}
//初始化   (使用该slot, 则 timer 将位于data线程中)
void Data::slot_init()
{
//    qDebug() << "dataThreadID:" << QThread::currentThreadId();
//    blockTypes = 7; //有7种块
//    grade = 0;  //成绩为0
//    flag_gameRunning = false;   //初始化游戏运行标志位
//    eachBlockTypes = new int[blockTypes]{2, 4, 2, 2, 4, 4, 1};   //初始化每种块的形状数
//    timerNum = 200; //定时器定时时间
//    timer = new QTimer;
//    connect(timer, SIGNAL(timeout()), this, SLOT(slot_timeOut()));
//    connect(this, SIGNAL(timerStart(int)), timer, SLOT(start(int)));
//    connect(this, SIGNAL(timerStop()), timer, SLOT(stop()));
}
//数据初始化(每次开始一场新游戏时,均需执行一次)
void Data::dataInit()
{
    for (int y=0; y<20; y++) {  //清除 gameData
        for (int x=0; x<10; x++) {
            gameData[y][x] = false;
        }
    }
    /* 生成第一块 */
    QTime time = QTime::currentTime();
    qsrand(time.msec()+time.second()*1000);
    currentBlockType[0] = qrand() % blockTypes;    //第一块的种类
    currentBlockType[1] = qrand() % eachBlockTypes[currentBlockType[0]]; //第一块为该类种哪个变形
    shape2Data(currentBlockType, currentBlock);   //根据下一块的类型, 将数据放入nextBlock

    createNextBlock();   //生成下一块
    current_x = 4;  //设置当前坐标
    current_y = -4;
    getCurrentBlockLimit(); //获取当前块的限制
}
//生成下一块
void Data::createNextBlock()
{
    QTime time = QTime::currentTime();
    qsrand(time.msec()+time.second()*100);
    nextBlockType[0] = qrand() % blockTypes;    //下一块的种类
    nextBlockType[1] = qrand() % eachBlockTypes[nextBlockType[0]]; //下一块为该类种哪个变形
    shape2Data(nextBlockType, nextBlock);   //根据下一块的类型, 将数据放入nextBlock
}
//将下一块 --> 当前块
void Data::next2current()
{
    flag_quickDown = false; //清除快速下降标志位
    current_x = 4;  //设置初始值
    current_y = -4;
    currentBlockType[0] = nextBlockType[0];
    currentBlockType[1] = nextBlockType[1];
    shape2Data(currentBlockType, currentBlock); //根据当前块的类型, 将数据放入 currentBlock
    getCurrentBlockLimit(); //获取当前块的限制
}
//获取游戏方块的限制
void Data::getCurrentBlockLimit()
{
    int tmp_b;
    int tmp_l;
    int tmp_r;
    for (int x=0; x<4; x++) {   //得到游戏方块每列最底部的块的y坐标
        tmp_b = -1;
        for (int y=0; y<4; y++) {
            if (currentBlock[y][x]) {
                tmp_b = tmp_b<y ? y : tmp_b;
            }
        }
        limit_b[x] = tmp_b;
        qDebug() << "limit_b" << limit_b[x];
    }
    for (int y=0; y<4; y++) {   //得到游戏方块左右的限制
        tmp_l = 4;
        tmp_r = -1;
        for (int x=0; x<4; x++) {
            if (currentBlock[y][x]) {
                tmp_l = tmp_l>x ? x : tmp_l;
                tmp_r = tmp_r>x ? tmp_r : x;
            }
        }
        limit_l[y] = tmp_l;
        limit_r[y] = tmp_r;
        qDebug() << "limit_x , y:" << limit_l[y] << limit_r[y];
    }
}
//检测是否可添加到根基
bool Data::detectionAddtoBasis()
{
    for (int x=0; x<4; x++) {
        for (int y=0; y<4; y++) {
            if (currentBlock[y][x]) {   //当前块
                if ((current_x+x < 0) || (current_y+y < 0)) {
                    continue;
                }
                if ((currentBlock[y][x] == true) && (gameData[current_y+y][current_x+x] == true)) {
                    qDebug() << tr("与地基重合 ---1");
                    return false;
                }
            }
        }
        if ((limit_b[x] >= 0) && (current_y+limit_b[x] >= 20)) {    //看是否与边框底部重合
                        qDebug() << tr("与边框底部重合 --2");
            return false;
        }
    }
    return true;
}
//检测是否可以移动
bool Data::detectionMoveIsSure()
{
    for (int i=0; i<4; i++) {   //与墙壁碰撞检测
        if (limit_l[i] <= 3 && current_x+limit_l[i] < 0) { //与左边碰撞
            return false;
        }
        if (limit_r[i] >= 0 && current_x+limit_r[i] >9) { //与右边碰撞
            return false;
        }
    }
    for (int y=0; y<4; y++) {   //与地基碰撞检测
        for (int x=0; x<4; x++) {
            //该游戏块 与 地基 重合
            if ((currentBlock[y][x] == true) && (gameData[current_y+y][current_x+x] == true)) {
                return false;
            }
        }
    }
    return true;
}
//将当前块添加到根基
void Data::addCurrentBlock2Basis()
{
    for (int y=0; y<4; y++) {
        for (int x=0; x<4; x++) {
            if (currentBlock[y][x]) {
                gameData[current_y+y][current_x+x] = true;
            }
        }
    }
    qDebug() << "Grade: " << grade;
}
//消除 行, 并加分 (可消除多行)
void Data::deleteALine()
{
    int deleteLine;     //存储可消除行
    while ((deleteLine=detectionDeleteALine()) != 20) { //当有可消除行时
        for (int y=deleteLine; y>=0; y--) {
            for (int x=0; x<10; x++) {
                if (y == 0) {
                    gameData[y][x] = false;
                } else {
                    gameData[y][x] = gameData[y-1][x];
                }
            }
        }
        grade++;    //成绩++
    }
}
//游戏停止
void Data::gameEnd(void)
{
    //timer->stop();
    emit timerStop();
    flag_gameRunning = false;   //游戏运行标志位
    emit gameOver();    //发送游戏结束信号
}
//暂停游戏
void Data::gamePause(void)
{
    qDebug() << "func: gamePause";
    if (flag_gameRunning) { //游戏正在运行
        flag_gameRunning = false;
//        timer->stop();
        emit timerStop();
    } else { //游戏暂停
        flag_gameRunning = true;
//        timer->start(timerNum);
        emit timerStart(timerNum);
    }

}
//检测是否可以消除一行块
int Data::detectionDeleteALine()
{
    for (int y=0; y<20; y++) {
        int num_have = 0;   //一行中方块的个数
        for (int x=0; x<10; x++) {
            if (gameData[y][x]) {
                num_have++;
            }
        }
        if (num_have == 10) {   //满一行
            return y;
        }
    }
    return 20;  //没有可以消除的行
}
//重新开始游戏
void Data::slot_gameBegin()
{
    flag_quickDown = false;
    if (flag_gameRunning == true) { //如果当前游戏正在运行,则关闭后重新开始
        gameEnd();
    }
    flag_gameRunning = true;
//    timer->start(timerNum); //开启定时器
    emit timerStart(timerNum);
    grade = 0;  //分数归零
    dataInit(); //初始化数据
}
//定时下降当前块 刷新UI
void Data::slot_timeOut()
{
  // qDebug() << "TimeOut_dataThreadID:" << QThread::currentThreadId();
   qDebug() << "current_x:y : "<< current_x << current_y;
//    timer->stop();
    emit timerStop();
    if (flag_gameRunning == false) {    //游戏停止运行, 不开启定时器
        return ;
    }
    for (int i=0; i<10; i++) {
        if (gameData[0][i]) {
            qDebug() << "GAMEOVER 02";
            gameEnd();  //结束游戏
            return ;
        }
    }
    current_y++;
    if (detectionAddtoBasis() == false) {   //可以添加到根基
        for (int i=0; i<10; i++) {
            if (gameData[1][i]) {
                for (int j=0; j<4; j++) {
                    if (limit_b[j] > 0) {   //当前块与根基重合后, 将结束游戏
                        qDebug() << "GAMEOVER 01";
                        gameEnd();  //结束游戏
                        return ;
                    }
                }
            }
        }
        current_y--;
        addCurrentBlock2Basis();//该块放置到根基
        next2current();     //生成新块
        createNextBlock();  //创建Next块
        deleteALine();      //删除满行, 并得分
        //getBasisLimit();    //获取根基的限制
    }
    emit updateUi();    //向UI程序发送 刷新UI 信号
    //qDebug() << "timeOut";
    if (flag_quickDown) { //快速下降
//        timer->start(30);
        emit timerStart(30);
    } else {
//        timer->start(timerNum);
            emit timerStart(timerNum);
    }
}
//改变当前块的形状
void Data::slot_changeBlcokShape()
{
    currentBlockType[1]++;
    if (currentBlockType[1] >= eachBlockTypes[currentBlockType[0]]) {
        currentBlockType[1] = 0;
    }
    shape2Data(currentBlockType, currentBlock);
    getCurrentBlockLimit(); //获取当前块限制
    if (detectionMoveIsSure() == false) {   //与周围碰撞, 则恢复到变形前的形状
        if (currentBlockType[1] == 0) {
            currentBlockType[1] = eachBlockTypes[currentBlockType[0]];
        }
        currentBlockType[1]--;
        shape2Data(currentBlockType, currentBlock);
        getCurrentBlockLimit(); //获取当前块限制
    }
    emit updateUi(); //更新UI
}
//处理GUI传递的按键事件
void Data::slot_processKeyEvent(dir_t dir)
{
       qDebug() << "Process_dataThreadID:" << QThread::currentThreadId();
    //timer->stop();
    switch (dir) {
    case LEFT:      // 左
        current_x--;
        //向左移动触碰到墙壁
        if (detectionMoveIsSure() == false) {
            current_x++;
        }
//        timer->start(timerNum);
        emit timerStart(timerNum);
        break;
    case RIGHT:     // 右
        current_x++;
        //向右移动触碰到墙壁
        if (detectionMoveIsSure() == false) {
            current_x--;
        }
        break;
    case CHANGE:    // 改变形状
        slot_changeBlcokShape();    //改变形状
        break;
    case BEGIN:         //开始新游戏
        slot_gameBegin();
        break;
    case END:           //结束游戏
        gameEnd();
        break;
    case PAUSE:         //暂停
        gamePause();
        qDebug() << "gamePause";
        break;
    case QUICK_DOWN:    //快速下降
        flag_quickDown = true;
        break;
    }
    //timer->start(timerNum);
    emit updateUi(); //更新UI
}

//根据 [0]类型 [1]该类型中的变化, 给newBlock填充数据
void Data::shape2Data(int *type, bool (*block)[4])
{
    for (int y=0; y<4; y++) {   //先清除
        for (int x=0; x<4; x++) {
            block[y][x] = false;
        }
    }
    switch (type[0]) {
    case 0: // ----
        switch (type[1]) {
        case 0:
            block[2][0] = true; block[2][1] = true; block[2][2] = true; block[2][3] = true;
            break;
        case 1:
            block[0][1] = true; block[1][1] = true; block[2][1] = true; block[3][1] = true;
            break;
        }
        break;
    case 1: // -*-
        block[2][1] = true;
        switch (type[1]) {
        case 0:
            block[1][1] = true; block[2][0] = true;  block[2][2] = true;
            break;
        case 1:
            block[2][2] = true; block[1][1] = true;  block[3][1] = true;
            break;
        case 2:
            block[3][1] = true; block[2][0] = true; block[2][2] = true;
            break;
        case 3:
            block[2][0] = true; block[1][1] = true; block[3][1] = true;
            break;
        }
        break;
    case 2: // -|_
        switch (type[1]) {
        case 0:
            block[1][0] = true; block[1][1] = true; block[2][1] = true; block[2][2] = true;
            break;
        case 1:

            block[1][2] = true; block[2][2] = true; block[2][1] = true; block[3][1] = true;
            break;
        }
        break;
    case 3: // _|-
        switch (type[1]) {
        case 0:
            block[1][3] = true; block[1][2] = true; block[2][2] = true; block[2][1] = true;
            break;
        case 1:
            block[1][1] = true; block[2][1] = true; block[2][2] = true; block[3][2] = true;
            break;
        }
        break;
    case 4: // |__
        switch (type[1]) {
        case 0:
            block[1][0] = true; block[2][0] = true; block[2][1] = true; block[2][2] = true;
            break;
        case 1:
            block[1][2] = true; block[1][1] = true; block[2][1] = true; block[3][1] = true;
            break;
        case 2:
            block[3][2] = true; block[2][0] = true; block[2][1] = true; block[2][2] = true;
            break;
        case 3:
            block[3][0] = true; block[3][1] = true; block[2][1] = true; block[1][1] = true;
            break;
        }
        break;
    case 5: // __|
        switch (type[1]) {
        case 0:
            block[1][2] = true; block[2][0] = true; block[2][1] = true; block[2][2] = true;
            break;
        case 1:
            block[3][2] = true; block[1][1] = true; block[2][1] = true; block[3][1] = true;
            break;
        case 2:
            block[3][0] = true; block[2][0] = true; block[2][1] = true; block[2][2] = true;
            break;
        case 3:
            block[1][0] = true; block[1][1] = true; block[2][1] = true; block[3][1] = true;
            break;
        }
        break;
    case 6: // 田
        block[1][1] = true; block[1][2] = true; block[2][1] = true; block[2][2] = true;
        break;
    }
}
